package com.software.graduationproject.generator;


import com.google.common.collect.Maps;
import com.v2coding.code.GeneratorConfig;
import com.v2coding.code.util.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import com.google.common.base.Strings;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;


import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 代码生成器
 */
@Slf4j
public class Generator {

    private static String DRIVER;
    private static String URL;
    private static String USERNAME;
    private static String PASSWORD;
    private static String JAVA_SOURCE_DIR;
    private static String PAGE_SOURCE_DIR;
    private static String JAVA_PACKAGE;
    private static String PAGE_PACKAGE;
    private static String TEMPLATE_DIR;
    private static List<String> IGNORE_FIELDS;
    private static String[] TABLES;

    public static void main(String[] args) throws Exception {
        generator_v2teching_gasmon();
    }

    public static void generator_v2teching_gasmon() {
        DRIVER = "com.mysql.cj.jdbc.Driver";
        URL = "jdbc:mysql://localhost:3306/graduation_project?useSSL=false";
        USERNAME = "root";
        PASSWORD = "123456";
        JAVA_SOURCE_DIR = "./generator/src/main/java/";
        PAGE_SOURCE_DIR = "./generator/src/";
        JAVA_PACKAGE = "com.software.graduationproject.modules";
        PAGE_PACKAGE = "/views/modules";
        TEMPLATE_DIR = "/templates.huanwei";
        IGNORE_FIELDS = Arrays.asList("id", "created_by", "created_date", "updated_by", "updated_date", "enabled");
        // 生成代码
        // ~ 生成全部
        // generatorFile(GenType.MBA);
        TABLES = new String[]{
                "sys_user_role"
        };

        TABLES = Arrays.stream(TABLES).filter(s -> !Strings.isNullOrEmpty(s)).toArray(String[]::new);
        generatorFile(GenType.MBA, TABLES);
        //generatorMenu(400, 600700, TABLES);

    }

    // ---------------------------------------------------------------------------------------------------

    private static VelocityContext CONTEXT = Velocitys.getContext();

    public static void generatorFile(GenType genType) {
        generatorFile(genType, null);
    }

    @SneakyThrows
    public static void generatorFile(GenType genType, String... tables) {
        FileUtils.deleteQuietly(new File(JAVA_SOURCE_DIR));
        FileUtils.deleteQuietly(new File(PAGE_SOURCE_DIR));
        Generator generator = new Generator();
        if (tables == null || tables.length == 0) {
            List<String> tabs = DBUtils.getTables(getCon());
            tables = tabs.toArray(new String[tabs.size()]);
        }
        for (String tab : tables) {
            Map<String, Object> data = Maps.newHashMap();
            DBUtils.getColumns(getCon(), tab, data);
            GeneratorConfig.Table table = generator.read(data);
            if (GenType.JPA.equals(genType)) {
                // Entity
                writeJava(generator, table.getEntityPackageFile(), TEMPLATE_DIR + "/entity.java.vm", CONTEXT);
                // EntityQo
                writeJava(generator, table.getEntityQoPackageFile(), TEMPLATE_DIR + "/entityqo.java.vm", CONTEXT);
                // EntityVo
                writeJava(generator, table.getEntityVoPackageFile(), TEMPLATE_DIR + "/entityvo.java.vm", CONTEXT);
                // Dao
                writeJava(generator, table.getEntityDaoPackageFile(), TEMPLATE_DIR + "/dao.java.vm", CONTEXT);
                // Service
                writeJava(generator, table.getEntityServicePackageFile(), TEMPLATE_DIR + "/service.java.vm", CONTEXT);
                // Controller
                writeJava(generator, table.getEntityControllerPackageFile(), TEMPLATE_DIR + "/controller.java.vm", CONTEXT);
                // vue
                writePage(generator, table.getEntityPagePackageFile(), TEMPLATE_DIR + "/vue.vm", CONTEXT);
            }

            if (GenType.MBA.equals(genType)) {
                // Entity
                writeJava(generator, table.getModelPackageFile(), TEMPLATE_DIR + "/model.java.vm", CONTEXT);
                // EntityQo
                writeJava(generator, table.getEntityQoPackageFile(), TEMPLATE_DIR + "/entityqo.java.vm", CONTEXT);
                writeJava(generator, table.getEntityCreateQoPackageFile(), TEMPLATE_DIR + "/entitycreateqo.java.vm", CONTEXT);
                writeJava(generator, table.getEntityUpdateQoPackageFile(), TEMPLATE_DIR + "/entityupdateqo.java.vm", CONTEXT);
                // EntityVo
                writeJava(generator, table.getEntityVoPackageFile(), TEMPLATE_DIR + "/entityvo.java.vm", CONTEXT);
                // Mapper
                writeJava(generator, table.getModelMapperPackageFile(), TEMPLATE_DIR + "/mapper.java.vm", CONTEXT);
                // MapperXml
                writeJava(generator, table.getModelMapperXmlPackageFile(), TEMPLATE_DIR + "/mapper.xml.vm", CONTEXT);
                // Service
                writeJava(generator, table.getEntityServicePackageFile(), TEMPLATE_DIR + "/service.java.vm", CONTEXT);
                // Controller
                writeJava(generator, table.getEntityControllerPackageFile(), TEMPLATE_DIR + "/controller.java.vm", CONTEXT);
                // vue
                writePage(generator, table.getEntityPagePackageFile(), TEMPLATE_DIR + "/vue.vm", CONTEXT);
            }
        }
    }


    public GeneratorConfig.Table read(Map<String, Object> entityMap) {
        GeneratorConfig.Table table = new GeneratorConfig.Table();
        String tableName = String.valueOf(entityMap.get("table"));      // system_user
        String moduleName = NameUtils.getModuleName(tableName);         // system
        String comment = String.valueOf(entityMap.get("comment"));      // 用户表
        table.setName(tableName);                                       // system_user
        table.setModule(moduleName);                                    // system
        table.setComment(comment);                                      // 用户表
        CONTEXT.put("Module", StringUtils.capitalize(moduleName));
        CONTEXT.put("comment", comment);

        // sys_user -> su
        StringBuilder tableSimple = new StringBuilder();
        for (String s : tableName.split("_")) {
            tableSimple.append(s.charAt(0));
        }
        CONTEXT.put("tableSimple", tableSimple);

        // Entity
        // --------------------------------------------------------------------------------------------------------------------
        String Entity = NameUtils.getEntityName(tableName);             // SystemUser
        String EntityQo = Entity + "Qo";                                // SystemUserQo
        String EntityCreateQo = Entity + "CreateQo";                                // SystemUserQo
        String EntityUpdateQo = Entity + "UpdateQo";                                // SystemUserQo
        String EntityVo = Entity + "Vo";                                // SystemUserVo
        String EntityDao = Entity + "Dao";                              // SystemUserDao
        String EntityService = Entity + "Service";                      // SystemUserService
        String EntityContoller = Entity + "Controller";                 // SystemUserController
        CONTEXT.put("Entity", Entity);
        CONTEXT.put("entity", StringUtils.uncapitalize(Entity));
        CONTEXT.put("EntityQo", EntityQo);
        CONTEXT.put("entityQo", StringUtils.uncapitalize(EntityQo));
        CONTEXT.put("EntityVo", EntityVo);
        CONTEXT.put("entityVo", StringUtils.uncapitalize(EntityVo));
        CONTEXT.put("EntityDao", EntityDao);
        CONTEXT.put("entityDao", StringUtils.uncapitalize(EntityDao));
        CONTEXT.put("EntityService", EntityService);
        CONTEXT.put("entityService", StringUtils.uncapitalize(EntityService));
        CONTEXT.put("EntityController", EntityContoller);
        CONTEXT.put("entityController", StringUtils.uncapitalize(EntityContoller));

        String Model = NameUtils.getEntityName(tableName);               // SystemUser
        String ModelMapper = Model + "Mapper";                           // SystemUserMapper
        String ModelService = Model + "Service";                         // SystemUserService
        String ModelController = Model + "Controller";                   // SystemUserController
        CONTEXT.put("Model", Model);
        CONTEXT.put("model", StringUtils.uncapitalize(Model));
        CONTEXT.put("ModelMapper", ModelMapper);
        CONTEXT.put("modelMapper", StringUtils.uncapitalize(ModelMapper));

        // Entity File
        // --------------------------------------------------------------------------------------------------------------------
        String entityFile = Entity + ".java";
        String entityQoFile = EntityQo + ".java";
        String entityCreateQoFile = EntityCreateQo + ".java";
        String entityUpdateQoFile = EntityUpdateQo + ".java";
        String entityVoFile = EntityVo + ".java";
        String entityDaoFile = EntityDao + ".java";
        String entityServiceFile = EntityService + ".java";
        String entityContollerFile = EntityContoller + ".java";
        String entityPageFileName = Entity + ".vue";
        String modelFile = Model + ".java";
        String modelMapperFile = ModelMapper + ".java";
        String modelMapperXmlFile = ModelMapper + ".xml";

        // Package
        // --------------------------------------------------------------------------------------------------------------------
        String packageEntity = getPackageName(JAVA_PACKAGE, moduleName, "entity");          // com.v2coding.project.system.entity
        String packageEntityQo = getPackageName(JAVA_PACKAGE, moduleName, "object");        // com.v2coding.project.system.object
        String packageEntityVo = getPackageName(JAVA_PACKAGE, moduleName, "object");        // com.v2coding.project.system.object
        String packageEntityDao = getPackageName(JAVA_PACKAGE, moduleName, "dao");          // com.v2coding.project.system.dao
        String packageEntityService = getPackageName(JAVA_PACKAGE, moduleName, "service");  // com.v2coding.project.system.service
        String packageEntityController = getPackageName(JAVA_PACKAGE, moduleName, "web");   // com.v2coding.project.system.web
        String packagePage = PAGE_PACKAGE;
        CONTEXT.put("packageEntity", packageEntity);
        CONTEXT.put("packageEntityQo", packageEntityQo);
        CONTEXT.put("packageEntityVo", packageEntityVo);
        CONTEXT.put("packageEntityDao", packageEntityDao);
        CONTEXT.put("packageEntityService", packageEntityService);
        CONTEXT.put("packageEntityController", packageEntityController);
        String packageModel = getPackageName(JAVA_PACKAGE, moduleName, "entity");            // com.v2coding.project.system.entity
        String packageModelMapper = getPackageName(JAVA_PACKAGE, moduleName, "mapper");      // com.v2coding.project.system.mapper
        String packageModelMapperXml = getPackageName(JAVA_PACKAGE, moduleName, "mapper");   // com.v2coding.project.system.mapper
        CONTEXT.put("packageModel", packageModel);
        CONTEXT.put("packageModelMapper", packageModelMapper);


        // Package File
        // --------------------------------------------------------------------------------------------------------------------
        String entityPackageFile = packageEntity.replace(".", "/") + "/" + entityFile;
        String entityQoPackageFile = packageEntityQo.replace(".", "/") + "/" + entityQoFile;
        String entityCreateQoPackageFile = packageEntityQo.replace(".", "/") + "/" + entityCreateQoFile;
        String entityUpdateQoPackageFile = packageEntityQo.replace(".", "/") + "/" + entityUpdateQoFile;
        String entityVoPackageFile = packageEntityQo.replace(".", "/") + "/" + entityVoFile;
        String entityDaoPackageFile = packageEntityDao.replace(".", "/") + "/" + entityDaoFile;
        String entityServicePackageFile = packageEntityService.replace(".", "/") + "/" + entityServiceFile;
        String entityControllerPackageFile = packageEntityController.replace(".", "/") + "/" + entityContollerFile;
        String entityPagePackageFile = PAGE_PACKAGE + "/" + StringUtils.capitalize(moduleName) + "/" + entityPageFileName;
        String modelPackageFile = packageModel.replace(".", "/") + "/" + modelFile;
        String modelMapperPackageFile = packageModelMapper.replace(".", "/") + "/" + modelMapperFile;
        String modelMapperXmlPackageFile = packageModelMapperXml.replace(".", "/") + "/" + modelMapperXmlFile;

        table.setEntityPackageFile(entityPackageFile);
        table.setEntityQoPackageFile(entityQoPackageFile);
        table.setEntityCreateQoPackageFile(entityCreateQoPackageFile);
        table.setEntityUpdateQoPackageFile(entityUpdateQoPackageFile);
        table.setEntityVoPackageFile(entityVoPackageFile);
        table.setEntityDaoPackageFile(entityDaoPackageFile);
        table.setEntityServicePackageFile(entityServicePackageFile);
        table.setEntityControllerPackageFile(entityControllerPackageFile);
        table.setEntityPagePackageFile(entityPagePackageFile);
        table.setModelPackageFile(modelPackageFile);
        table.setModelMapperPackageFile(modelMapperPackageFile);
        table.setModelMapperXmlPackageFile(modelMapperXmlPackageFile);
        CONTEXT.put("table", table);
        // --------------------------------------------------------------------------------------------------------------------
        List<Map<String, Object>> attrs = (List<Map<String, Object>>) entityMap.get("attrs");
        List<GeneratorConfig.Table.Field> fields = new ArrayList();
        for (Map<String, Object> attr : attrs) {
            GeneratorConfig.Table.Field field = new GeneratorConfig.Table.Field();
            String columnName = attr.get("name").toString();
            if (IGNORE_FIELDS.contains(columnName)) {
                continue;
            }
            field.setName(NameUtils.getFieldName(columnName));
            field.setColumnName(columnName);
            field.setGetset(StringUtils.capitalize(field.getName()));
            String className = attr.get("className").toString();
            if (DBUtils.DB_TYPE_MAP.containsKey(className)) {
                className = DBUtils.DB_TYPE_MAP.get(className);
            }
            field.setClassName(StringUtils.substringAfterLast(className, "."));
            Boolean isAutoIncrement = Boolean.valueOf(attr.get("isAutoIncrement").toString());
            field.setIsAutoImcrement(isAutoIncrement);
            field.setLength(Integer.valueOf(attr.get("length").toString()));
            DbColumnType columnType = MySqlTypeConvert.processTypeConvert(attr.get("columnTypeName").toString());
            field.setColumnType(columnType.toString());
            //            Console.log(columnType.toString() + "-----" + attr.get("columnType").toString() + "-----" + attr.get("className").toString() + "-----" + attr.get("columnTypeName").toString());

            if (null != attr.get("colDecimals")) {
                field.setColDecimals(Integer.valueOf(attr.get("colDecimals").toString()));
                field.setLength(field.getLength() - field.getColDecimals());
            }

            field.setNotNull(!Boolean.valueOf(attr.get("nullable").toString()));
            if (null != attr.get("remarks")) {
                field.setComment(String.valueOf(attr.get("remarks")));
            }
            if (table.getPrimaryField() == null && isAutoIncrement) {
                table.setPrimaryField(field);
            } else {
                fields.add(field);
            }
            if (log.isTraceEnabled()) {
                log.trace(field.toString());
            }
            //            Console.log(field);
        }
        String result = fields.stream().map(f -> f.getColumnName()).collect(Collectors.joining(", "));

        table.setColumns(result);
        table.setFields(fields);
        return table;
    }

    public static void writeJava(Generator generator, String packageFileName, String templateFileName, VelocityContext context) throws IOException {
        System.out.println("writeJava:" + JAVA_SOURCE_DIR + packageFileName);
        generator.write(JAVA_SOURCE_DIR + packageFileName, Velocitys.toString(templateFileName, context));
    }

    public static void writePage(Generator generator, String packageFileName, String templateFileName, VelocityContext context) throws IOException {
        System.out.println("writePage:" + PAGE_SOURCE_DIR + packageFileName);
        generator.write(PAGE_SOURCE_DIR + packageFileName, Velocitys.toString(templateFileName, context));
    }

    public void write(String target, String content) throws IOException {
        File file = new File(target);
        File directory = file.getParentFile();
        if (!directory.exists()) {
            directory.mkdirs();
        }
        file.createNewFile();
        FileWriter fw = new FileWriter(file);
        BufferedWriter writer = new BufferedWriter(fw);
        writer.write(content);
        writer.flush();
        writer.close();
        fw.close();
    }

    public static Connection getCon() throws SQLException {
        DbUtils.loadDriver(DRIVER);
        return DriverManager.getConnection(URL, USERNAME, PASSWORD);
    }

    public static String getPackageName(String base, String module, String type) {
        if (StringUtils.isEmpty(module)) {
            return base + "." + type;
        }
        return base + "." + module + "." + type;
    }

    public static void printlnTable(String... tables) throws Exception {
        if (tables == null || tables.length == 0) {
            List<String> tabs = DBUtils.getTables(getCon());
            Collections.sort(tabs);
            tables = tabs.toArray(new String[tabs.size()]);
        }
        for (String tab : tables) {
            System.out.println("\"" + tab + "\",");
        }
    }

    public static void printlnModule() throws Exception {
        Map<String, List<String>> modules = new HashMap<>();
        Set<String> mods = new HashSet<>();
        List<String> tbls = DBUtils.getTables(getCon());
        Collections.sort(tbls);
        String[] tables = tbls.toArray(new String[tbls.size()]);
        for (String tab : tables) {
            String mod = StringUtils.substringBefore(tab, "_");
            mods.add(mod);
        }
        for (String md : mods) {
            List<String> ts = new ArrayList();
            for (String table : tables) {
                if (StringUtils.startsWith(table, md)) {
                    ts.add(table);
                }
            }
            modules.put(md, ts);
        }
        System.out.println(modules);
    }

//    /**
//     * generatorMenu 生成菜单所有表
//     *
//     * @param mid : 模块起始ID
//     */
//    public static void generatorMenu(int mid) {
//        generatorMenu(mid, 0, null);
//    }
//
//    /**
//     * generatorMenu 生成菜单指定表
//     *
//     * @param mid    : 模块起始ID
//     * @param tid    : 表起始ID
//     * @param tables : 待生成的表名
//     *               <pre>
//     *                                                                                                                                                                                                                           TABLES = new String[]{
//     *                                                                                                                                                                                                                                   "pms_project", "pms_facility"
//     *                                                                                                                                                                                                                           };
//     *                                                                                                                                                                                                                           generatorMenu(20000, 20100, TABLES);
//     *                                                                                                                                                                                                                           > 'Pms' : 20000
//     *                                                                                                                                                                                                                           > 'PmsProject' : 20100
//     *                                                                                                                                                                                                                           > 'PmsFacility' : 20200
//     *                                                                                                                                                                                                                   </pre>
//     */
//    @SneakyThrows
//    public static void generatorMenu(int mid, int tid, String... tables) {
//        Map<String, List<String>> modules = new HashMap<>();
//        boolean isGenerateAll = false;
//        if (null == tables || tables.length == 0) {
//            List<String> tbls = DBUtils.getTables(getCon());
//            tables = tbls.toArray(new String[tbls.size()]);
//            isGenerateAll = true;
//        }
//        for (String tab : tables) {
//            String md = StringUtils.substringBefore(tab, "_");
//            List<String> ts = new ArrayList();
//            for (String table : tables) {
//                if (StringUtils.startsWith(table, md)) {
//                    ts.add(table);
//                }
//            }
//            modules.put(md, ts);
//        }
//        System.out.println("----------------------------------------------------------------------------------------");
//        System.out.println("menusql");
//        System.out.println("----------------------------------------------------------------------------------------");
//        String output;
//        List<Integer> idstr = new ArrayList<>();
//        // mid module id
//        // pid page id
//        // cid child id
//        for (String module : modules.keySet()) {
//            String Module = StringUtils.capitalize(module);// System
//            output = "INSERT INTO `sys_menu` VALUES (" + mid + ", " + "0" + ", " + "'" + Module + "'" + ", " + "'/" + Module + "'" + ", " + "'/" + Module + "'" + ", " + "1" + ", " + "'" + Module + "'" + ", " + "'el-icon-fa-list-ul'" + ", " + mid + ", " + "1" + ", " + "'admin', '2020-01-23 00:00:00', 'admin', '2020-01-23 00:00:00', 'Y'); ";
//            System.out.println(output);
//            idstr.add(mid);
//            for (String table : modules.get(module)) {
//                if (isGenerateAll) {
//                    tid = mid + 100;
//                }
//                String Entity = StringUtils.capitalize(NameUtils.getEntityName(table));
//                output = "INSERT INTO `sys_menu` VALUES (" + tid + ", " + mid + ", " + "'" + Entity + "'" + "," + "'/" + Module + "/" + Entity + "'" + ", " + "'/" + Module + "/" + Entity + "'" + ", " + "2" + ", " + "'" + Module + ":" + Entity + "'" + ", " + "'el-icon-fa-list-ul'" + ", " + tid + ", " + "1" + ", " + "'admin', '2020-01-23 00:00:00', 'admin', '2020-01-23 00:00:00', 'Y'); ";
//                System.out.println(output);
//                idstr.add(tid);
//                int cid = tid + 1;
//                output = "INSERT INTO `sys_menu` VALUES (" + cid + ", " + tid + ", " + "'" + "新增" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "create" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "create" + "'" + ", " + "3" + ", " + "'" + Module + ":" + Entity + ":" + "create" + "'" + ", " + "'el-icon-fa-list-ul'" + ", " + cid + ", " + "1" + ", " + "'admin', '2020-01-23 00:00:00', 'admin', '2020-01-23 00:00:00', 'Y'); ";
//                System.out.println(output);
//                idstr.add(cid);
//                cid++;
//
//                output = "INSERT INTO `sys_menu` VALUES (" + cid + ", " + tid + ", " + "'" + "修改" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "update" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "update" + "'" + ", " + "3" + ", " + "'" + Module + ":" + Entity + ":" + "update" + "'" + ", " + "'el-icon-fa-list-ul'" + ", " + cid + ", " + "1" + ", " + "'admin', '2020-01-23 00:00:00', 'admin', '2020-01-23 00:00:00', 'Y'); ";
//                System.out.println(output);
//                idstr.add(cid);
//                cid++;
//
//                output = "INSERT INTO `sys_menu` VALUES (" + cid + ", " + tid + ", " + "'" + "删除" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "delete" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "delete" + "'" + ", " + "3" + ", " + "'" + Module + ":" + Entity + ":" + "delete" + "'" + ", " + "'el-icon-fa-list-ul'" + ", " + cid + ", " + "1" + ", " + "'admin', '2020-01-23 00:00:00', 'admin', '2020-01-23 00:00:00', 'Y'); ";
//                System.out.println(output);
//                idstr.add(cid);
//                cid++;
//
//                output = "INSERT INTO `sys_menu` VALUES (" + cid + ", " + tid + ", " + "'" + "导出" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "export" + "'" + ", " + "'/" + Module + "/" + Entity + "/" + "export" + "'" + ", " + "3" + ", " + "'" + Module + ":" + Entity + ":" + "export" + "'" + ", " + "'el-icon-fa-list-ul'" + ", " + cid + ", " + "1" + ", " + "'admin', '2020-01-23 00:00:00', 'admin', '2020-01-23 00:00:00', 'Y'); ";
//                System.out.println(output);
//                idstr.add(cid);
//                tid = tid + 100;
//            }
//            mid = mid + 10000;
//        }
//        System.out.println("----------------------------------------------------------------------------------------");
//        System.out.println("menuids");
//        System.out.println("----------------------------------------------------------------------------------------");
//        for (Integer id : idstr) {
//            System.out.println("INSERT INTO `sys_role_menu`(`role_id`, `menu_id`, `enabled`, `created_by`, `created_date`, `updated_by`, `updated_date`) VALUES ( 1, " + id + ", 'Y', 'UNKNOWN', '2020-09-11 13:42:53', 'UNKNOWN', '2020-09-11 13:42:53');");
//        }
//    }

}
